import { FIEngine } from "./FIEngine.js";
import { FIIMousePos } from "./FIIMousePos.js";
export interface FIIPoint {
  x: number;
  y: number;
}
export interface FIIPointerEventHandler { (ev: PointerEvent | MouseEvent | TouchEvent): void }
export interface FIIMousePosHandler { (e: FIIMousePos): void }
export class FIMouseBtn {
  readonly id: number
  isDown = false;
  onDown?: FIIMousePosHandler;
  onMove?: FIIMousePosHandler;
  onUp?: FIIMousePosHandler;
  constructor(id: number) {
    this.id = id;
  }

}
export class FIMouse {
  private _engine: FIEngine;
  private _canvas: HTMLCanvasElement;

  private _btns: { [key in number]: FIMouseBtn } = {
    0: new FIMouseBtn(0),
    1: new FIMouseBtn(1),
    2: new FIMouseBtn(2),
    3: new FIMouseBtn(3),
    4: new FIMouseBtn(4),
  }
  private _onMouseMove?: FIIMousePosHandler;
  private _touch?: Touch

  private _mouseX = 0;
  private _mouseY = 0;
  speed = 0.5
  private _onmousedown: FIIPointerEventHandler = (e) => {
    if (e instanceof TouchEvent) {
      if (!this._touch) {
        this._touch = e.touches[0];
        const { x, y } = this.mapElementToCanvas({
          x: this._touch.pageX,
          y: this._touch.pageY
        });
        this._btns[0].onDown && this._btns[0].onDown({ x, y });
      }
    } else {
      const { x, y } = this.getMouseXYFromEvent(e);
      const btn = this._btns[e.button];
      if (!btn.isDown) {
        btn.isDown = true;
        btn.onDown && btn.onDown({ x, y });
      }
    }
  };
  private _onmousemove: FIIPointerEventHandler = (e) => {
    if (e instanceof TouchEvent) {
      if (this._touch) {
        this._touch = e.touches[0];
        const { x, y } = this.mapElementToCanvas({
          x: this._touch.pageX,
          y: this._touch.pageY
        });
        this._btns[0].onMove && this._btns[0].onMove({ x, y });
      }
    } else {
      const { x, y } = this.getMouseXYFromEvent(e);
      for (const k in this._btns) {
        const btn = this._btns[k];
        btn.isDown && btn.onMove && btn.onMove({ x, y })
      }
      this._onMouseMove && this._onMouseMove({ x, y })
    }
  };
  private _onmouseup: FIIPointerEventHandler = (e) => {
    if (e instanceof TouchEvent) {
      if (this._touch) {
        const { x, y } = this.mapElementToCanvas({
          x: this._touch.pageX,
          y: this._touch.pageY
        });
        this._btns[0].onUp && this._btns[0].onUp({ x, y });
      }
    } else {
      const { x, y } = this.getMouseXYFromEvent(e);
      const btn = this._btns[e.button];
      btn.isDown = false
      btn.onUp && btn.onUp({ x, y });
    }
  };
  private _canvasResizeObserver?: ResizeObserver;
  private _canvasRect: DOMRectReadOnly;
  get onLBDown(): FIIMousePosHandler | undefined {
    return this._btns[0].onDown;
  }
  set onLBDown(v: FIIMousePosHandler | undefined) {
    this._btns[0].onDown = v;
  }
  get onLBMove(): FIIMousePosHandler | undefined {
    return this._btns[0].onMove;
  }
  set onLBMove(v: FIIMousePosHandler | undefined) {
    this._btns[0].onMove = v;
  }
  get onLBUp(): FIIMousePosHandler | undefined {
    return this._btns[0].onUp;
  }
  set onLBUp(v: FIIMousePosHandler | undefined) {
    this._btns[0].onUp = v;
  }
  get onMBDown(): FIIMousePosHandler | undefined {
    return this._btns[1].onDown;
  }
  set onMBDown(v: FIIMousePosHandler | undefined) {
    this._btns[1].onDown = v;
  }
  get onMBMove(): FIIMousePosHandler | undefined {
    return this._btns[1].onMove;
  }
  set onMBMove(v: FIIMousePosHandler | undefined) {
    this._btns[1].onMove = v;
  }
  get onMBUp(): FIIMousePosHandler | undefined {
    return this._btns[1].onUp;
  }
  set onMBUp(v: FIIMousePosHandler | undefined) {
    this._btns[1].onUp = v;
  }
  get onRBDown(): FIIMousePosHandler | undefined {
    return this._btns[2].onDown;
  }
  set onRBDown(v: FIIMousePosHandler | undefined) {
    this._btns[2].onDown = v;
  }
  get onRBMove(): FIIMousePosHandler | undefined {
    return this._btns[2].onMove;
  }
  set onRBMove(v: FIIMousePosHandler | undefined) {
    this._btns[2].onMove = v;
  }
  get onRBUp(): FIIMousePosHandler | undefined {
    return this._btns[2].onUp;
  }
  set onRBUp(v: FIIMousePosHandler | undefined) {
    this._btns[2].onUp = v;
  }

  getOnBtnDown(id: number): FIIMousePosHandler | undefined {
    return this._btns[id].onDown;
  }
  setOnBtnDown(id: number, v: FIIMousePosHandler | undefined) {
    this._btns[id].onDown = v;
  }
  getOnBtnMove(id: number): FIIMousePosHandler | undefined {
    return this._btns[id].onMove;
  }
  setOnBtnMove(id: number, v: FIIMousePosHandler | undefined) {
    this._btns[id].onMove = v;
  }
  getOnBtnUp(id: number): FIIMousePosHandler | undefined {
    return this._btns[id].onUp;
  }
  setOnBtnUp(id: number, v: FIIMousePosHandler | undefined) {
    this._btns[id].onUp = v;
  }
  get onMouseMove(): FIIMousePosHandler | undefined {
    return this._onMouseMove;
  }
  set onMouseMove(value: FIIMousePosHandler | undefined) {
    this._onMouseMove = value;
  }
  constructor(engine: FIEngine) {
    this._engine = engine;
    this._canvas = this._engine.canvas;
    this._canvasRect = this._canvas.getBoundingClientRect();
    this.init();
  }
  getMouseXYFromEvent(e: MouseEvent | PointerEvent): FIIPoint {
    if (document.pointerLockElement) {
      this._mouseX += e.movementX * this.speed;
      this._mouseY += e.movementY * this.speed;
      const { width, height } = this._canvasRect;
      if (this._mouseX > width) {
        this._mouseX = width;
      } else if (this._mouseX < 0) {
        this._mouseX = 0;
      }
      if (this._mouseY > height) {
        this._mouseY = height;
      } else if (this._mouseY < 0) {
        this._mouseY = 0;
      }
    } else {
      this._mouseX = e.offsetX;
      this._mouseY = e.offsetY;
    }
    return this.mapElementToCanvas({
      x: this._mouseX,
      y: this._mouseY
    })
  }
  mapElementToCanvas(v: FIIPoint): FIIPoint {
    const { width: eleWidth, height: eleHeight } = this._canvasRect;
    const { width, height } = this._canvas;
    const x = v.x * width / eleWidth;
    const y = v.y * height / eleHeight;
    return { x, y };
  }

  init(): FIMouse {
    const canvas = this._engine.canvas;
    canvas.addEventListener('touchstart', this._onmousedown);
    canvas.addEventListener('touchmove', this._onmousemove);
    canvas.addEventListener('touchend', this._onmouseup);
    canvas.addEventListener('touchcancel', this._onmouseup);
    
    canvas.addEventListener('pointerdown', this._onmousedown);
    canvas.addEventListener('pointermove', this._onmousemove);
    canvas.addEventListener('pointerup', this._onmouseup);

    this._canvasResizeObserver = new ResizeObserver((entries) => {
      this._canvasRect = entries[0].contentRect;
    })
    this._canvasResizeObserver.observe(canvas)
    return this;
  }
  destory(): FIMouse {
    const canvas = this._engine.canvas;
    canvas.removeEventListener('pointerdown', this._onmousedown);
    canvas.removeEventListener('pointermove', this._onmousemove);
    canvas.removeEventListener('pointerup', this._onmouseup);
    this._canvasResizeObserver?.disconnect()
    return this;
  }
}
